﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Diagnostics;
using System.Globalization;

namespace FishServe.Client.Application.Manifest
{
    internal class ReleaseManifest
    {
        private string _name;
        private string _version;
        private ReleaseManifestType _type;
        private DateTime _createdDateTime;
        private string _guid;
        private List<ModuleManifest> _moduleManifests = new List<ModuleManifest>();
        private string _filename;

        public string Name
        {
            get { return _name; }
        }

        public string Version
        {
            get { return _version; }
        }

        public ReleaseManifestType Type
        {
            get { return _type; }
        }

        public DateTime CreatedDateTime
        {
            get { return _createdDateTime; }
        }

        public string Guid
        {
            get{ return _guid; }
        }

        public string Filename
        {
            get { return _filename; }
        }

        public ModuleManifest[] ModuleManifests
        {
            get { return _moduleManifests.ToArray<ModuleManifest>(); }
        }

        internal ReleaseManifest(string filename, XElement releaseManifestElement)
        {
            _filename = filename;

            XAttribute attribute;

            attribute = releaseManifestElement.Attribute("Name");
            Trace.Assert(attribute != null && !String.IsNullOrEmpty(attribute.Value), "Name attribute missing from <ReleaseManifest> element");
            _name = attribute.Value;

            attribute = releaseManifestElement.Attribute("Version");
            Trace.Assert(attribute != null && !String.IsNullOrEmpty(attribute.Value), "Version attribute missing from <ReleaseManifest> element");
            _version = attribute.Value;

            attribute = releaseManifestElement.Attribute("Type");
            Trace.Assert(attribute != null && !String.IsNullOrEmpty(attribute.Value), "Type attribute missing from <ReleaseManifest> element");
            try
            {
                _type = (ReleaseManifestType)Enum.Parse(typeof(ReleaseManifestType), attribute.Value);
            }
            catch (Exception ex)
            {
                throw new Exception(String.Format("Invalid Type attribute '{0}' in <ReleaseManifest> element", attribute.Value), ex);
            }

            attribute = releaseManifestElement.Attribute("Created");
            Trace.Assert(attribute != null && !String.IsNullOrEmpty(attribute.Value), "Created attribute missing from <ReleaseManifest> element");
            if (!DateTime.TryParseExact(attribute.Value, "yyyy-MM-dd HHHH:mm:ss", null, DateTimeStyles.None, out _createdDateTime))
                throw new Exception(String.Format("Failed to parse <ReleaseManifest> Created date/time '{0}'", attribute.Value));

            attribute = releaseManifestElement.Attribute("Guid");
            Trace.Assert(attribute != null && !String.IsNullOrEmpty(attribute.Value), "Guid attribute missing from <ReleaseManifest> element");
            _guid = attribute.Value;
            
            XElement moduleManifestsElement = releaseManifestElement.Element("ModuleManifests");
            Trace.Assert(moduleManifestsElement != null, "Expected <ModuleManifests> element in <ReleaseManifest>");

            XElement[] moduleManifestElements = moduleManifestsElement.Elements("ModuleManifest").ToArray<XElement>();
            Trace.Assert( moduleManifestElements.Length != 0, "Expected ModuleManifest element(s) in <ReleaseManifest> <ModuleManifests> element");

            foreach (XElement moduleManifestElement in moduleManifestElements)
            {
                attribute = moduleManifestElement.Attribute("ManifestType");
                Trace.Assert( attribute != null && !String.IsNullOrEmpty( attribute.Value ), "Missing ManifestType attribute in <ModuleManifest> element.");

                ModuleManifestType moduleManifestType;
                try
                {
                    moduleManifestType = (ModuleManifestType)Enum.Parse(typeof(ModuleManifestType), attribute.Value);
                }
                catch (Exception ex)
                {
                    throw new Exception(String.Format("Invalid Type attribute '{0}' in <ModuleManifest> element", attribute.Value), ex);
                }

                ModuleManifest moduleManifest = null;
                switch( moduleManifestType )
                {
                    case ModuleManifestType.Application :
                        moduleManifest = new ApplicationModuleManifest( moduleManifestElement );
                        break;
                    case ModuleManifestType.Shell :
                        moduleManifest = new ModuleManifest( moduleManifestElement );
                        break;
                    case ModuleManifestType.AddIn : 
                        moduleManifest = new ModuleManifest( moduleManifestElement );
                        break;
                    case ModuleManifestType.Module :
                        moduleManifest = new ModuleManifest( moduleManifestElement );
                        break;
                    case ModuleManifestType.Content :
                        moduleManifest = new ModuleManifest( moduleManifestElement );
                        break;
                    default :
                        throw new Exception( String.Format( "Unhandled module manifest type '{0}'", moduleManifestType.ToString() ));
                }
                _moduleManifests.Add(moduleManifest);
            }
        }
    }
}
